home *** CD-ROM | disk | FTP | other *** search
- /*************************************************************************************************
- *
- *
- * ObjectMacZapp -- a standard Mac OOP application template
- *
- *
- *
- * ZBlockFile.cpp -- a generic file object that includes some basic block
- * management facilities. This type of file is useful for
- * implementing VM schemes and databases, etc.
- *
- *
- * © 1996, Graham Cox
- *
- *
- *
- *
- *************************************************************************************************/
-
-
- #include "ZBlockFile.h"
- #include "ZArray.h"
- #include "ZErrors.h"
- #include "ZDefines.h"
-
-
-
- ZBlockFile::ZBlockFile( const FSSpec& aFileSpec )
- : ZFile( aFileSpec )
- {
- bmap = NULL;
- refSeed = 1;
- useCount = 0;
- }
-
-
- ZBlockFile::ZBlockFile( Str255 fName )
- : ZFile( fName )
- {
- bmap = NULL;
- refSeed = 1;
- useCount = 0;
- }
-
-
- ZBlockFile::~ZBlockFile()
- {
- if ( bmap )
- ForgetObject( bmap );
- }
-
-
- /*------------------------------------*** OPEN ***------------------------------------*/
- /*
- open the file and initialise the map, from file if this is overridden to do so.
- ----------------------------------------------------------------------------------------*/
-
- void ZBlockFile::Open()
- {
- ZFile::Open();
-
- InitMap();
- ReadMap();
- }
-
-
-
- /*-------------------------------*** GETBLOCKCOUNT ***--------------------------------*/
- /*
- return the total number of blocks
- ----------------------------------------------------------------------------------------*/
-
- long ZBlockFile::GetBlockCount()
- {
- if ( bmap )
- return bmap->CountItems();
- else
- return 0;
- }
-
-
- /*--------------------------------*** GETBLOCKREF ***---------------------------------*/
- /*
- get the ref for the indexed block
- ----------------------------------------------------------------------------------------*/
-
- long ZBlockFile::GetBlockRef( const long bkIndex )
- {
- Block b;
-
- if ( bmap )
- {
- bmap->GetArrayItem( &b, bkIndex );
-
- return b.fRefNum;
- }
- else
- return -1;
- }
-
-
- /*------------------------------------*** CLOSE ***-----------------------------------*/
- /*
- write header and map, then close the file as normal
- ----------------------------------------------------------------------------------------*/
-
- void ZBlockFile::Close()
- {
- WriteHeader();
- WriteMap();
-
- ZFile::Close();
-
- ForgetObject( bmap );
- bmap = NULL;
- }
-
-
- /*----------------------------------*** ADDBLOCK ***----------------------------------*/
- /*
- register a new block in the file's map. This allocates a ref num and returns it.
- ----------------------------------------------------------------------------------------*/
-
- long ZBlockFile::AddBlock()
- {
- long aRef = GetNewRefSeed();
-
- AddBlockUsingRef( aRef );
-
- return aRef;
- }
-
-
- /*------------------------------*** ADDBLOCKUSINGREF ***------------------------------*/
- /*
- register a new block using the ref passed. Caller MUST ensure this is unique.
- ----------------------------------------------------------------------------------------*/
-
- void ZBlockFile::AddBlockUsingRef( const long aRef )
- {
- if ( GetBlock( aRef ) == 0 )
- {
- useCount++;
-
- // initially we set the size to zero, since we don't know until the data is written
- // how big it is. This is fine, since a) we don't want to expand the file until we
- // have to, and b) SetBlockData automatically manages the reallocation of blocks as
- // needed to satisfy the size request.
-
- AppendBlock( aRef, 0 );
- }
- else
- FailOSErr( kBlockDupRefErr );
- }
-
-
- /*--------------------------------*** REMOVEBLOCK ***---------------------------------*/
- /*
- unregister block from file, returning the space it occupies as a free block.
- ----------------------------------------------------------------------------------------*/
-
- void ZBlockFile::RemoveBlock( const long ref )
- {
- long i;
-
- if ( i = GetBlock( ref ) > 0 )
- {
- useCount--;
-
- FreeBlock( i );
- CompactMap();
- }
- else
- FailOSErr( kBlockNotFoundErr );
- }
-
-
- /*--------------------------------*** GETBLOCKDATA ***--------------------------------*/
- /*
- get the data of a block into an arbitrary buffer.
- ----------------------------------------------------------------------------------------*/
-
- void ZBlockFile::GetBlockData( const long ref, void* dataPtr, long* dataLen )
- {
- long i = GetBlock( ref );
- long iSize;
-
- if ( i > 0 )
- {
- iSize = GetBlockSize( i );
-
- SetMarkToBlock( i );
-
- iSize = MIN( iSize, *dataLen );
- Read((Ptr) dataPtr, &iSize );
-
- *dataLen = iSize;
-
- }
- else
- FailOSErr( kBlockNotFoundErr );
- }
-
-
- /*--------------------------------*** GETBLOCKDATA ***--------------------------------*/
- /*
- get the data of a blovk into an existing handle, replacing its contents and resizing as
- necessary
- ----------------------------------------------------------------------------------------*/
-
- void ZBlockFile::GetBlockData( const long ref, Handle dataH )
- {
- char hs;
- long iSize, i;
-
- FailNILParam( dataH );
- i = GetBlock( ref );
-
- if ( i > 0 )
- {
- hs = HGetState( dataH );
-
- iSize = GetBlockSize( i );
-
- SetHandleSize( dataH, iSize );
- FailOSErr( MemError());
-
- SetMarkToBlock( i );
-
- HLock( dataH );
- Read( *dataH, &iSize );
-
- HSetState( dataH, hs );
- }
- else
- FailOSErr( kBlockNotFoundErr );
- }
-
-
- /*--------------------------------*** SETBLOCKDATA ***--------------------------------*/
- /*
- put data from an arbitrary buffer into the file, associated with the block ref. This
- splits or reallocates blocks as needed.
- ----------------------------------------------------------------------------------------*/
-
- void ZBlockFile::SetBlockData( const long ref, void* dataPtr, long dataLen )
- {
- long iSize, i;
-
- i = GetBlock( ref );
-
- if ( i > 0 )
- {
- iSize = GetBlockSize( i );
-
- // if the size of the block doesn't match <dataLen>, we may need to reallocate
- // or split the block
-
- if ( iSize != dataLen )
- {
- ReallocBlock( ref, dataLen );
-
- i = GetBlock( ref );
-
- if ( i <= 0 )
- FailOSErr( kBlockBadRefErr );
-
- iSize = GetBlockSize( i );
-
- CompactMap();
- }
-
- // save the data to the block
-
- SetMarkToBlock( i );
- Write((Ptr) dataPtr, &iSize );
- }
- else
- FailOSErr( kBlockNotFoundErr );
-
- }
-
-
- /*--------------------------------*** SETBLOCKDATA ***--------------------------------*/
- /*
- put data from a handle into the file associated with the block ref.
- ----------------------------------------------------------------------------------------*/
-
- void ZBlockFile::SetBlockData( const long ref, Handle dataH )
- {
- char hs;
-
- hs = HGetState( dataH );
- HLock( dataH );
-
- try
- {
- SetBlockData( ref, *dataH, GetHandleSize( dataH ));
- }
- catch( OSErr err )
- {
- HSetState( dataH, hs );
-
- throw err;
- }
-
- HSetState( dataH, hs );
- }
-
-
- /*--------------------------------*** APPENDBLOCK ***---------------------------------*/
- /*
- assign a new block to contain the bytes requested. This initially tries to find a free
- block big enough, splitting it if necessary, otherwise it appends a block to the end of
- the file, growing it.
- ----------------------------------------------------------------------------------------*/
-
- void ZBlockFile::AppendBlock( const long ref, const unsigned long sizeNeeded )
- {
- Block b;
- long i;
-
- i = FindFreeBlock( sizeNeeded );
-
- if ( i > 0 )
- {
- // we found a block big enough to contain the data, so let's see what's in it:
-
- bmap->GetArrayItem( &b, i );
-
- // need to split it?
-
- if ( sizeNeeded < b.fSize )
- SplitBlock( i, sizeNeeded );
-
- // set up entry- the mark & length fields are already set, so don't touch it:
-
- b.fRefNum = ref;
- b.fStatus = notFree;
-
- bmap->SetArrayItem( &b, i );
- }
- else
- {
- // no block was found that can contain the size requested, so we'll have to append
- // a new block to the end of the file.
-
- b.fRefNum = ref;
- b.fStatus = notFree;
- b.fMark = GetLength();
- b.fSize = sizeNeeded;
-
- // grow the file to the desired length
-
- SetLength( b.fMark + sizeNeeded );
-
- // add item to map
-
- bmap->AppendItem( &b );
- }
- }
-
-
- /*-------------------------------*** FINDFREEBLOCK ***--------------------------------*/
- /*
- returns the index of a free block large enough to contain the bytes passed, or 0 if none
- could be found.
- ----------------------------------------------------------------------------------------*/
-
- long ZBlockFile::FindFreeBlock( const unsigned long sizeNeeded )
- {
- // locate a free block that can contain <sizeNeeded>.
-
- long i;
- Block b;
- Boolean found = FALSE;
-
- for ( i = 1; i <= bmap->CountItems(); i++ )
- {
- bmap->GetArrayItem( &b, i );
-
- if (( b.fSize <= sizeNeeded ) && ( b.fStatus == free ))
- {
- found = TRUE;
- break;
- }
- }
-
- if ( found )
- return i;
- else
- return 0;
- }
-
-
- /*----------------------------------*** GETBLOCK ***----------------------------------*/
- /*
- return the map index of a block with the given ref. This is a simple search through the
- map.
- ----------------------------------------------------------------------------------------*/
-
- long ZBlockFile::GetBlock( const long ref )
- {
- // locate the block with the given ref
-
- long i;
- Block b;
- Boolean found = FALSE;
-
- for ( i = 1; i <= bmap->CountItems(); i++ )
- {
- bmap->GetArrayItem( &b, i );
-
- if ( b.fRefNum == ref )
- {
- found = TRUE;
- break;
- }
- }
-
- if ( found )
- return i;
- else
- return 0;
- }
-
-
- /*---------------------------------*** FREEBLOCK ***----------------------------------*/
- /*
- mark the block with the index passed as available for re-use.
- ----------------------------------------------------------------------------------------*/
-
- void ZBlockFile::FreeBlock( const long bkIndex )
- {
- // mark the block with the index passed as free.
-
- Block b;
-
- bmap->GetArrayItem( &b, bkIndex );
-
- b.fRefNum = 0;
- b.fStatus = free;
-
- // if the block size is zero, it may as well be deleted from the map, since
- // it is only cluttering it up and is not managing any space in the file.
-
- if ( b.fSize == 0 )
- bmap->DeleteItem( bkIndex );
- else
- bmap->SetArrayItem( &b, bkIndex );
- }
-
-
- /*---------------------------------*** SPLITBLOCK ***---------------------------------*/
- /*
- divide a block into a free part and a used part. This is done whenever a block becomes
- smaller.
- ----------------------------------------------------------------------------------------*/
-
- void ZBlockFile::SplitBlock( const long bkIndex, const unsigned long bkSize )
- {
- // splits a block in two, returning unused space to the pool.
-
- Block b, subBlock;
-
- bmap->GetArrayItem( &b, bkIndex );
-
- // the sub-block will manage the bytes left over once <bkSize> has been
- // alloted to this block.
-
- subBlock.fRefNum = 0;
- subBlock.fMark = b.fMark + bkSize;
- subBlock.fSize = b.fSize - bkSize;
- subBlock.fStatus = free;
-
- // sanity check- subBlock can't be 0 or negative:
-
- FailOSErr(( subBlock.fSize <= 0 )? kBadSplitReqErr : noErr );
-
- // now change block to exact size
-
- b.fSize = bkSize;
-
- // add and update map items
-
- bmap->AppendItem( &subBlock );
- bmap->SetArrayItem( &b, bkIndex );
- }
-
-
- /*--------------------------------*** REALLOCBLOCK ***--------------------------------*/
- /*
- reassign the block <ref> to a new physical place in the file. This is done whenever the
- data size changes, and it attempts to reuse space where possible.
- ----------------------------------------------------------------------------------------*/
-
- void ZBlockFile::ReallocBlock( const long ref, long newBkSize )
- {
- // reassign the data to another block, since it's size has changed.
-
- long i;
- Block b;
-
- i = GetBlock( ref );
-
- if ( i > 0 )
- {
- bmap->GetArrayItem( &b, i );
-
- // if new size is LESS than block, we can simply split it, returning the excess to
- // the free space.
-
- if ( newBkSize < b.fSize )
- SplitBlock( i, newBkSize );
- else
- {
- // new data is LARGER than before, so we free the whole block and allocate a
- // new one.
-
- FreeBlock( i );
- AppendBlock( ref, newBkSize );
- }
- }
- else
- FailOSErr( kBlockNotFoundErr );
- }
-
-
- /*--------------------------------*** GETBLOCKSIZE ***--------------------------------*/
- /*
- return the size of the indexed block
- ----------------------------------------------------------------------------------------*/
-
- unsigned long ZBlockFile::GetBlockSize( const long bkIndex )
- {
- Block b;
-
- bmap->GetArrayItem( &b, bkIndex );
- return b.fSize;
- }
-
-
- /*-------------------------------*** SETMARKTOBLOCK ***-------------------------------*/
- /*
- position the filemark at the place alloted by the indexed block.
- ----------------------------------------------------------------------------------------*/
-
- void ZBlockFile::SetMarkToBlock( const long bkIndex )
- {
- Block b;
-
- bmap->GetArrayItem( &b, bkIndex );
- SetMark( b.fMark );
- }
-
-
- /*-----------------------------------*** INITMAP ***----------------------------------*/
- /*
- initialise the block map (creates it anew)
- ----------------------------------------------------------------------------------------*/
-
- void ZBlockFile::InitMap()
- {
- FailNIL( bmap = new ZArray( sizeof( Block )));
- }
-
-
- /*--------------------------------*** COMPACTMAP ***----------------------------------*/
- /*
- conjoin adjacent free blocks into larger free blocks, thus increasing their chances of
- being reused. This is a moderately efficient operation in that it does not change anything
- in the file itself, but simply modifies the map.
- ----------------------------------------------------------------------------------------*/
-
- void ZBlockFile::CompactMap()
- {
- // TO DO
- }